import numpy as np
import math

from xml.dom import minidom
import svg.path as svgp

#import matplotlib.pyplot as plt
import os

# 一些具体参数：
# 采样与计算
fineness = 200 # 采样精细度

# 渲染与绘制
drawLine = True               # 是否绘制傅里叶展开辅助线
drawCount = 250               # 绘制图像的张数(实际绘制直线的段数)
widthAndHeight = [1920, 1080] # svg canvas大小
opacitylevel = 10             # 不透明度级数(主要函数曲线)
threshold = 0.3               # 阈值(主要函数曲线)
width = 3                     # 线宽(主要函数曲线)
color = (255, 239, 36)        # 颜色(主要函数曲线)
width2 = 0.3                  # 线宽(辅助线)
bassisopacity = 0.25          # 透明度(辅助线)
color2 = (51, 173, 255)       # 颜色(辅助线)


#  一个用于获取3阶贝塞尔曲线描点的函数
def dotsFromBezier3(bezier3Curve = [complex(0, 0), complex(0, 0), complex(0, 0), complex(0, 0)], n = 1):
    changeBezier3Curve = [(bezier3Curve[0].real, bezier3Curve[0].imag),
                          (bezier3Curve[1].real, bezier3Curve[1].imag),
                          (bezier3Curve[2].real, bezier3Curve[2].imag),
                          (bezier3Curve[3].real, bezier3Curve[3].imag)]
    inputs = np.array(changeBezier3Curve)
    def Bezier3(t):
        bezierCof = np.array([
                [-1, 3, -3, 1],[3, -6, 3, 0],
                [-3, 3, 0, 0],[1, 0, 0, 0]])
        return np.array([t**3, t**2, t**1, 1]).dot(bezierCof.dot(inputs))
    points = []
    for i in range(0, n+1):
        p = tuple(Bezier3(float(i)/n))
        points += [p]
    return points


#  读取只含3阶贝塞尔曲线的SVG文件并转换为贝塞尔曲线集
with open('TestSVG.svg', 'r') as f:
    paths = (minidom.parse(f)).getElementsByTagName("path")
    curve = []
    for i, path in enumerate(paths):
        analysis_paths = svgp.parse_path(path.attributes['d'].value)
        for p in analysis_paths:
            if type(p) is svgp.path.Move:
                continue
            elif type(p) is svgp.path.CubicBezier:
                curve += [[p.start, p.control1, p.control2, p.end]]
            elif type(p) is svgp.path.Line:
                curve += [[p.start, p.start, p.end, p.end]]
            else:
                continue
print('曲线:', len(curve))


#  用获取3阶贝塞尔曲线描点的函数从曲线集中汲取点列
points = []
for cvs in curve:
    pointsInACurve = dotsFromBezier3(cvs, fineness)
    points += pointsInACurve
pointsNum = len(points)
print("点数:", pointsNum)


#  得到近似的傅里叶函数
#  将点集转换为复数集
complexPoints = []
for point in points:
    complexPoints += [complex(point[0], point[1])]


#  计算傅里叶函数的系数并得到近似的傅里叶函数
def coeOfFourier(n=0, CP = [complex(0.0, 0.0)]):
    deltat = 1 / (len(CP))
    return sum([CP[t] * (complex(np.cos(- n * 2 * np.pi * t * deltat),
                                 np.sin(- n * 2 * np.pi * t * deltat)
                                 )
                         ) * deltat for t in range(len(CP))])
def coeOfFourierSlist(CP = [complex(0.0, 0.0)], n = 1):
    X = [[coeOfFourier(t, CP) for t in range(n)]]
    Y = [[coeOfFourier(- t, CP) for t in range(n)]]
    Z = [[n]]
    return (Y + X + Z)
def FourierFunction(t=0, COF = [complex(0.0, 0.0)]):
    return sum(((COF[0][Cn]) * complex(np.cos(- Cn * 2 * np.pi * t),
                                 np.sin(- Cn * 2 * np.pi * t))
               +
               (COF[1][Cn]) * complex(np.cos(Cn * 2 * np.pi * t),
                                 np.sin(Cn * 2 * np.pi * t)))
               for Cn in range(1, COF[2][0])) + ((COF[0][0]) * complex(1, 0))

print('C_0:', coeOfFourier(0, complexPoints))


#  函数给出的点集：
CFSL = coeOfFourierSlist(complexPoints, 50)
pointsByFourierComplex = [FourierFunction(t/drawCount, CFSL) for t in range(drawCount)]
pointsByFourier = [tuple([comp.real, comp.imag]) for comp in pointsByFourierComplex]


#  暂时的可视化工具
'''print(CFSL)
print(pointsByFourier)
plt.figure("ex")
pointsX = [pointsByFourier[t][0] for t in range(len(pointsByFourier))]
pointsY = [pointsByFourier[t][1] for t in range(len(pointsByFourier))]
plt.plot(pointsX, pointsY, '-')
plt.show()'''

#  可视化
#  文件操作
'''if os.path.exists('dataFiles'):
    file = open('dataFiles/data.svg', 'w')
else:
    os.mkdir('dataFiles')
    file = open('dataFiles/data.svg', 'w')'''
#  写入svg文件头
head = [
    '<svg  version="1.1" xmlns="http://www.w3.org/2000/svg" xml:space="preserve" width="',
    str(widthAndHeight[0]), '" height="', str(widthAndHeight[1]), '" >\n']
#  file.writelines(head)
#  写入SVG路径
def writePathLines(k, i, P, Linestyle, Wh= [1920, 1080], cfs = [[(0+0j)], [(0+0j)], [1]]):
    Cent = (cfs[0][0].real - Wh[0]/2, cfs[0][0].imag - Wh[1]/2)
    if k <= i <= len(P):
        Lines = ['\t<path style="'] + Linestyle + ['" d="\n', '\t\t']
        for t in range(k, i):
            if t == k:
                Lines += ['M', str(P[t][0] - Cent[0]), ' ', str(P[t][1] - Cent[1])]
            else:
                Lines += ['L', str(P[t][0] - Cent[0]), ' ', str(P[t][1] - Cent[1]), '\n']
        Lines += ['" />\n']
    return Lines
#  渐隐
def DissolvesLines(P, opacitylevel, threshold, width=2, color=(0,0,0), cfs=[[(0+0j)], [(0+0j)], [1]], Wh=[1920, 1080]):
    length = len(P)
    interval = math.floor(length*(1-threshold)/opacitylevel)
    Lines1 = []
    for i in range(opacitylevel):
        opacity = (i+1)/opacitylevel
        styledata = ['fill:none;stroke:rgb', str(color),
                     ';stroke-width:', str(width), ';stroke-opacity:', str(opacity)]
        if i == (opacitylevel-1):
            Lines1 += writePathLines(i*interval, length, P, ['fill:none;stroke:rgb', str(color),
                                                             ';stroke-width:', str(width),
                                                             ';stroke-opacity:', str(opacity)], Wh, cfs)
        else:
            Lines1 += writePathLines(i*interval, (i+1)*interval, P, styledata, Wh, cfs)
    return Lines1

#  file.writelines(DissolvesLines(pointsByFourier[0:500], 10, 0.3, 3, Wh=widthAndHeight, cfs=CFSL))
#  写入圆与箭头
def arrowAndCircle(center=(0, 0), endPoint=(0, 0), angle=0.75, clockwise=1, opacity=0.5, color=(0,0,0), n=0, wid=2):
    radius = abs(complex(center[0]-endPoint[0], center[1]-endPoint[1]))
    a = (math.sin(angle * 2 * math.pi), math.cos(angle * 2 * math.pi))
    arrow = ['\n\n<defs>\n\t<marker id="arrow', str(n), '" viewBox="0 0 20 20" refX="0.15" refY="2.5"\n',
             '\tmarkerWidth="10" markerHeight="10" orient="auto" style="fill:rgb', str(color),
             ';stroke-width:0;fill-opacity:', str(opacity), '">\n',
             '\t\t<path d="M 0 0 L 10 2.5 L 0 5 z" />\n', '\t</marker>\n</defs> ']
    ac = arrow + ['\n\t<path style="fill:none;stroke:rgb', str(color),
                  ';stroke-width:', str(wid), ';stroke-opacity:', str(opacity), '" d="\n']
    if angle > 0.5:
        ac += ['M', str(endPoint[0]), ' ', str(endPoint[1]),
               'A', str(radius), ' ', str(radius), ' 0 1 ', str(clockwise), ' ',
               str((endPoint[0] - center[0]) * a[1] - (endPoint[1] - center[1]) * a[0] + center[0]), ' ',
               str((endPoint[0] - center[0]) * a[0] - (endPoint[1] - center[1]) * a[1] + center[1]), ' \n']
    else:
        ac += ['M', str(endPoint[0]), ' ', str(endPoint[1]),
               'A', str(radius), ' ', str(radius), ' 0 0 ', str(clockwise), ' ',
               str((endPoint[0] - center[0]) * a[1] - (endPoint[1] - center[1]) * a[0] + center[0]), ' ',
               str((endPoint[0] - center[0]) * a[0] - (endPoint[1] - center[1]) * a[1] + center[1]), ' \n']
    ac += ['" />\n']
    ac += ['\t<path style="fill:none;stroke:rgb', str(color), ';stroke-width:', str(wid), ';stroke-opacity:',
           str(opacity), '" marker-end="url(#arrow', str(n), ')" d="\n']
    ac += ['M', str(center[0]), ' ',
           str(center[1]), 'L',
           str(endPoint[0]), ' ',
           str(endPoint[1]), '"\n/>']
    return ac
def visFourierByCircles(cfs=[[(0+0j)], [(0+0j)], [1]], Wh= [1920, 1080], t=0,
                        bassisopacity=0.5, color=(0,0,0), wid=2):
    Cent = (cfs[0][0].real - Wh[0] / 2, cfs[0][0].imag - Wh[1] / 2)
    def Fouriercoe(n=1): return complex(math.cos(n * 2 * math.pi * t), math.sin(n * 2 * math.pi * t))
    vFC = []
    opa = (1/cfs[2][0]) * 1/bassisopacity
    en, an = cfs[0][0], cfs[0][0]
    for k in range(1, cfs[2][0]):
        en += cfs[0][k] * Fouriercoe(k)
        vFC += arrowAndCircle(center=(an.real - Cent[0], an.imag - Cent[1]),
                              endPoint=(en.real - Cent[0], en.imag - Cent[1]),
                              opacity=k * opa + (1 - bassisopacity), n=k,  color=color, wid=wid-(k/cfs[2][0])*wid)
        en += cfs[1][k] * Fouriercoe(-k)
        an += cfs[0][k] * Fouriercoe(k)
        vFC += arrowAndCircle(center=(an.real - Cent[0], an.imag - Cent[1]),
                              endPoint=(en.real - Cent[0], en.imag - Cent[1]),
                              opacity=k * opa + (1 - bassisopacity), n=k, color=color, wid=wid-(k/cfs[2][0])*wid)
        an += cfs[1][k] * Fouriercoe(-k)
    return vFC
#  file.writelines(visFourierByCircles(CFSL, t=0.5, wid=0.25))
#  输出svg序列
pnum = len(pointsByFourier)
for i in range(pnum):
    ospath = ''.join(['dataFiles/data', str(i), '.svg'])
    pbf = pointsByFourier[i:] + pointsByFourier[:i]
    if os.path.exists('dataFiles'):
        file2 = open(ospath, 'w')
    else:
        os.mkdir('dataFiles')
        file2 = open(ospath, 'w')
    file2.writelines(head)
    file2.writelines(DissolvesLines(pbf, opacitylevel, threshold, width, Wh=widthAndHeight, cfs=CFSL, color=color))
    if drawLine:
        file2.writelines(visFourierByCircles(CFSL, t=pnum-((i+1)/pnum), wid=width2, bassisopacity=bassisopacity, color=color2))
    file2.write('</svg>')
    print('here:', i, '/', pnum)